package com.jht.bletool2java.characteristic.ftms;

import android.bluetooth.BluetoothGattCharacteristic;
import android.util.Log;

import androidx.annotation.Keep;

import com.jht.bletool2java.characteristic.TranslateData;
import com.jht.bletool2java.util.ByteUtil;
import com.jht.bletool2java.util.ComputerUtil;

import java.util.LinkedList;
import java.util.Queue;

import top.codestudy.annotation_uuid.MyUUID;

//判断处理ok
@Keep
@MyUUID(uuid = "00002ace-0000-1000-8000-00805f9b34fb")
public class CrossTrainerData implements TranslateData {
    private static CrossTrainerData data;
    private final String TAG = "CrossTrainerData";
    byte[] value1;
    private byte[] flags = new byte[3];
    private byte[] instantaneous_speed;
    private byte[] average_speed;
    private byte[] total_distance;
    private byte[] step_per_minute;
    private byte[] average_step_rate;
    private byte[] stride_count;
    private byte[] positive_elevation_gain;
    private byte[] negative_elevation_gain;
    private byte[] inclination;
    private byte[] ramp_angle_setting;
    private byte[] resistance_level;
    private byte[] instantaneous_power;
    private byte[] average_power;
    private byte[] total_energy;
    private byte[] energy_per_hour;
    private byte[] energy_per_minute;
    private byte[] heart_rate;
    private byte[] metabolic_equivalent;
    private byte[] elapsed_time;
    private byte[] remaining_time;
    private boolean InsSpeed = false;
    private boolean avgSpeed = false;
    private boolean totalDistance = false;
    private boolean StepCountPresent = false;
    private boolean strideCount = false;
    private boolean elevationGain = false;
    private boolean inclinationAndRampAngleSetting = false;
    private boolean resistanceLevel = false;
    private boolean insPower = false;
    private boolean avgPower = false;
    private boolean energy = false;
    private boolean heartRate = false;
    private boolean metabolic = false;
    private boolean elapsedTime = false;
    private boolean remainingTime = false;
    private boolean movementDirection = false; //Forward or Backward

    private int expect_data_length = 0;
    private byte[] valueData;
    private boolean isInvalidData = false;

    public CrossTrainerData() {
    }
    public CrossTrainerData(BluetoothGattCharacteristic characteristic) {
        byte[] value = characteristic.getValue();
        value1 = value;
        valueData = value;
        parseData(value);
        for (int i = 0; i < value.length; i++) {
            Log.i(TAG, "CrossTrainerData: i = " + i + "; ox" + Integer.toHexString(ByteUtil.byte1ToInt(value[i])));
        }
    }

    public static CrossTrainerData getInstance() {
        data = new CrossTrainerData();
        return data;
    }

    public void parseData(byte[] buffer) {
        System.arraycopy(buffer, 0, flags, 0, 3);
        checkEachFieldIfSupported();
        parseEachSupportedFeature(buffer);
    }

    private void checkEachFieldIfSupported() {

        byte firstbyte = (byte) (flags[0] ^ 0xFD); // 0xFD means all features are supported in 1st byte;
        byte secondbyte = (byte) (flags[1] ^ 0xFF); // 0xFF mean all features are supported in 2nd byte;
        byte threebyte = (byte) (flags[2] ^ 0xFF);

        expect_data_length = 3;

        if (isBitZero(firstbyte, 0)) {
            //instantaneous speed is supported
            InsSpeed = true;
            instantaneous_speed = new byte[2];
            expect_data_length += 2;
            Log.d(TAG, "instantaneous speed is supported");
        } else {
            Log.e(TAG, "数据过多！显示数据不可靠！");
        }
        if (isBitZero(firstbyte, 1)) {
            //average speed is supported
            avgSpeed = true;
            average_speed = new byte[2];
            expect_data_length += 2;
            Log.d(TAG, "average speed is supported");
        }

        if (isBitZero(firstbyte, 2)) {
            //totalDistance is supported
            totalDistance = true;
            total_distance = new byte[3];
            expect_data_length += 3;
            Log.d(TAG, "totalDistance is supported");
        }
        if (isBitZero(firstbyte, 3)) {
            //Step Count present is supported
            StepCountPresent = true;
            step_per_minute = new byte[2];
            average_step_rate = new byte[2];
            expect_data_length += 4;
            Log.d(TAG, "Step Count present is supported");
        }
        if (isBitZero(firstbyte, 4)) {
            //Stride Count present is supported
            strideCount = true;
            stride_count = new byte[2];
            expect_data_length += 2;
            Log.d(TAG, "Stride Count present is supported");
        }
        if (isBitZero(firstbyte, 5)) {
            //elevationGain is supported
            elevationGain = true;
            positive_elevation_gain = new byte[2];
            negative_elevation_gain = new byte[2];
            expect_data_length += 4;
            Log.d(TAG, "elevationGain is supported");
        }
        if (isBitZero(firstbyte, 6)) {
            //inclinationAndRampAngleSetting is supported
            inclinationAndRampAngleSetting = true;
            inclination = new byte[2];
            ramp_angle_setting = new byte[2];
            expect_data_length += 4;
            Log.d(TAG, "inclinationAndRampAngleSetting power is supported");
        }
        if (isBitZero(firstbyte, 7)) {
            //resistance level is supported
            resistanceLevel = true;
            resistance_level = new byte[2];
            expect_data_length += 2;
            Log.d(TAG, "resistance level is supported");
        }
        if (isBitZero(secondbyte, 0)) {
            // insPower features are supported
            insPower = true;
            instantaneous_power = new byte[2];
            expect_data_length += 2;
            Log.d(TAG, "insPower is supported");
        }
        if (isBitZero(secondbyte, 1)) {
            //avgPower is supported
            avgPower = true;
            average_power = new byte[2];
            expect_data_length += 2;
            Log.d(TAG, "avgPower is supported");
        }
        if (isBitZero(secondbyte, 2)) {
            //energy is supported
            energy = true;
            total_energy = new byte[2];
            energy_per_hour = new byte[2];
            energy_per_minute = new byte[1];
            expect_data_length += 5;
            Log.d(TAG, "energy is supported");
        }
        if (isBitZero(secondbyte, 3)) {
            // heartRate are supported
            heartRate = true;
            heart_rate = new byte[1];
            expect_data_length += 1;
            Log.d(TAG, "heartRate is supported");
        }
        if (isBitZero(secondbyte, 4)) {
            //metabolic is supported
            metabolic = true;
            metabolic_equivalent = new byte[1];
            expect_data_length += 1;
            Log.d(TAG, "metabolic is supported");
        }


        if (isBitZero(secondbyte, 5)) {
            //elapsed time is supported
            elapsedTime = true;
            elapsed_time = new byte[2];
            expect_data_length += 2;
            Log.d(TAG, "elapsed time is supported");
        }
        if (isBitZero(secondbyte, 6)) {
            //remaining time is supported
            remainingTime = true;
            remaining_time = new byte[2];
            expect_data_length += 2;
            Log.d(TAG, "remaining time is supported");
        }
        if (isBitZero(secondbyte, 7)) {
            //movementDirection is supported
            movementDirection = true;
            Log.d(TAG, "movementDirection time is Backward ");
        } else {
            Log.d(TAG, "movementDirection time is Forward ");
        }

    }


    /**
     * @param value, the xor result, 0 means feature is supported. 1 means feature is not supported
     * @param bit,   the bit of value to check
     * @return
     */
    private boolean isBitZero(byte value, int bit) {
        Queue queue = new LinkedList();
        String s = "";

        int power2 = (int) Math.pow(2, bit);
        int tmp = value & power2;
        if (tmp == 0) {
            return true;
        } else {
            return false;
        }
    }

    private void parseEachSupportedFeature(byte[] buffer) {
        if (buffer.length < expect_data_length) {
            isInvalidData = true;
            Log.d(TAG, "buffer length is " + buffer.length);
            Log.d(TAG, "expect data length is " + expect_data_length);
            return;
        }

        int startIndex = 3;
        if (InsSpeed) {
            System.arraycopy(buffer, startIndex, instantaneous_speed, 0, 2);
            startIndex += 2;
        }
        if (avgSpeed) {
            System.arraycopy(buffer, startIndex, average_speed, 0, 2);
            startIndex += 2;
        }
        if (totalDistance) {
            System.arraycopy(buffer, startIndex, total_distance, 0, 3);
            startIndex += 3;
        }
        if (StepCountPresent) {
            System.arraycopy(buffer, startIndex, step_per_minute, 0, 2);
            startIndex += 2;
            System.arraycopy(buffer, startIndex, average_step_rate, 0, 2);
            startIndex += 2;
        }
        if (strideCount) {
            System.arraycopy(buffer, startIndex, stride_count, 0, 2);
            startIndex += 2;
        }

        if (elevationGain) {
            System.arraycopy(buffer, startIndex, positive_elevation_gain, 0, 2);
            startIndex += 2;
            System.arraycopy(buffer, startIndex, negative_elevation_gain, 0, 2);
            startIndex += 2;
        }

        if (inclinationAndRampAngleSetting) {
            System.arraycopy(buffer, startIndex, inclination, 0, 2);
            startIndex += 2;
            System.arraycopy(buffer, startIndex, ramp_angle_setting, 0, 2);
            startIndex += 2;
        }

        if (resistanceLevel) {
            System.arraycopy(buffer, startIndex, resistance_level, 0, 2);
            startIndex += 2;
        }

        if (insPower) {
            System.arraycopy(buffer, startIndex, instantaneous_power, 0, 2);
            startIndex += 2;
        }
        if (avgPower) {
            System.arraycopy(buffer, startIndex, average_power, 0, 2);
            startIndex += 2;
        }
        if (energy) {
            System.arraycopy(buffer, startIndex, total_energy, 0, 2);
            startIndex += 2;
            System.arraycopy(buffer, startIndex, energy_per_hour, 0, 2);
            startIndex += 2;
            System.arraycopy(buffer, startIndex, energy_per_minute, 0, 1);
            startIndex += 1;
        }

        if (heartRate) {
            System.arraycopy(buffer, startIndex, heart_rate, 0, 1);
            startIndex += 1;
        }

        if (metabolic) {
            System.arraycopy(buffer, startIndex, metabolic_equivalent, 0, 1);
            startIndex += 1;
        }
        if (elapsedTime) {
            System.arraycopy(buffer, startIndex, elapsed_time, 0, 2);
            startIndex += 2;
        }
        if (remainingTime) {
            System.arraycopy(buffer, startIndex, remaining_time, 0, 2);
        }
    }

    public double getInstantaneousSpeed() {
        if (InsSpeed) {
            return ComputerUtil.multiply(ByteUtil.bytes2ToInt(instantaneous_speed, 0), "0.01");
        } else {
            return -9999;
        }
    }

    public double getAverageSpeed() {
        if (avgSpeed) {
            return ComputerUtil.multiply(ByteUtil.bytes2ToInt(average_speed, 0), "0.01");
        } else {
            return -9999;
        }
    }

    public int getTotalDistance() {
        if (totalDistance) {
            return ByteUtil.byte3ToInt(total_distance, 0);
        } else {
            return -9999;
        }
    }

    public int getStepPerMinute() {

        if (StepCountPresent) {
            return ByteUtil.bytes2ToInt(step_per_minute, 0);
        } else {
            return -9999;
        }
    }

    public int getAverageStepRate() {

        if (StepCountPresent) {
            return ByteUtil.bytes2ToInt(average_step_rate, 0);
        } else {
            return -9999;
        }
    }

    public double getStrideCount() {
        if (strideCount) {
            return ComputerUtil.multiply(ByteUtil.bytes2ToInt(stride_count, 0), "0.1");
        } else {
            return -9999;
        }
    }

    public int getPositiveElevationGain() {
        if (elevationGain) {
            return ByteUtil.bytes2ToInt(positive_elevation_gain, 0);
        } else {
            return -9999;
        }
    }

    public int getNegativeElevationGain() {
        if (elevationGain) {
            return ByteUtil.bytes2ToInt(negative_elevation_gain, 0);
        } else {
            return -9999;
        }
    }

    public double getInclination() {
        if (inclinationAndRampAngleSetting) {
            return ComputerUtil.multiply(ByteUtil.bytes2ToInt(inclination, 0), "0.1");
        } else {
            return -9999;
        }
    }

    public double getRampAngleSetting() {
        if (inclinationAndRampAngleSetting) {
            return ComputerUtil.multiply(ByteUtil.bytes2ToInt(ramp_angle_setting, 0), "0.1");
        } else {
            return -9999;
        }
    }

    public double getResistanceLevel() {
        if (resistanceLevel) {
            return ComputerUtil.multiply(ByteUtil.bytes2ToInt(resistance_level, 0), "0.1");
        } else {
            return -9999;
        }
    }

    public int getInstantaneousPower() {
        if (insPower) {
            return ByteUtil.bytes2ToInt(instantaneous_power, 0);
        } else {
            return -9999;
        }
    }

    public int getAveragePower() {
        if (avgPower) {
            return ByteUtil.bytes2ToInt(average_power, 0);
        } else {
            return -9999;
        }
    }

    public int getTotalEnergy() {
        if (energy) {
            return ByteUtil.bytes2ToInt(total_energy, 0);
        } else {
            return -9999;
        }
    }

    public int getEnergyPerHour() {
        if (energy) {
            return ByteUtil.bytes2ToInt(energy_per_hour, 0);
        } else {
            return -9999;
        }
    }

    public int getEnergyPerMinute() {
        if (energy) {
            return ByteUtil.byte1ToInt(energy_per_minute[0]);
        } else {
            return -9999;
        }
    }

    public int getHeartRate() {
        if (heartRate) {
            return ByteUtil.byte1ToInt(heart_rate[0]);
        } else {
            return -9999;
        }
    }

    public double getMetabolicEquivalent() {
        if (metabolic) {
            return ComputerUtil.multiply(ByteUtil.byte1ToInt(metabolic_equivalent[0]), "0.1");
        } else {
            return -9999;
        }
    }

    public int getElapedTime() {
        if (elapsedTime) {
            return ByteUtil.bytes2ToInt(elapsed_time, 0);
        } else {
            return -9999;
        }
    }

    public int getRemainingTime() {
        if (remainingTime) {
            return ByteUtil.bytes2ToInt(remaining_time, 0);
        } else {
            return -9999;
        }
    }

    public String getMovementDirection() {
        if (movementDirection) {
            return "Backward";
        } else {
            return "Forward";
        }
    }

    public String convert2String() {
        return "instant speed :  " + getInstantaneousSpeed()
                + "   km/h ;\n average speed :  " + getAverageSpeed()
                + "   km/h ;\n total distance :  " + getTotalDistance()
                + "   m ;\n TotalEnergy :  " + getTotalEnergy()
                + "   Calorie ;\n Instantaneous Power : " + getInstantaneousPower()
                + "   watt ;\n Average Power : " + getAveragePower()
                + "   watt ;\n inclination :  " + getInclination()
                + "   percentage ;\n HeartRate :  " + getHeartRate()
                + "   bpm ;\n elapsed time :  " + getElapedTime()
                + "   second ;\n MovementDirection :   " + getMovementDirection()
                + "  ;\n StepPerMinute :  " + getStepPerMinute()
                + "  ;\n StrideCount :  " + getStrideCount()
                + "  ;\n AverageStepRate :  " + getAverageStepRate()
                + "  ;\n ResistanceLevel :  " + getResistanceLevel()
                + "   ;\n Energy Per Hour : " + getEnergyPerHour()
                + "  calorie;\n Energy Per Minute : " + getEnergyPerMinute()
                + "  calorie;\n RemainingTime :  " + getRemainingTime()
                + "  second;\n Positive Elevation Gain : " + getPositiveElevationGain()
                + "  metre;\n Negative Elevation Gain : " + getNegativeElevationGain()
                + "  metre;\n Ramp Angle Setting : " + getRampAngleSetting()
                + "  Degree;\n Metabolic Equivalent: " + getMetabolicEquivalent()
                + "\n "
                ;
    }

    @Override
    public String merge(TranslateData translateData) {
        CrossTrainerData temp = (CrossTrainerData) translateData;
        if (temp.InsSpeed) {
            this.instantaneous_speed = temp.instantaneous_speed;
            this.InsSpeed = true;
        }
        if (temp.avgSpeed) {
            this.average_speed = temp.average_speed;
            this.avgSpeed = true;
        }
        if (temp.totalDistance) {
            this.total_distance = temp.total_distance;
            this.totalDistance = true;
        }
        if (temp.StepCountPresent) {
            this.step_per_minute = temp.step_per_minute;
            this.average_step_rate = temp.average_step_rate;
            this.StepCountPresent = true;
        }
        if (temp.strideCount) {
            this.stride_count = temp.stride_count;
            this.strideCount = true;
        }
        if (temp.elevationGain) {
            this.positive_elevation_gain = temp.positive_elevation_gain;

            this.negative_elevation_gain = temp.negative_elevation_gain;
            this.elevationGain = true;
        }

        if (temp.inclinationAndRampAngleSetting) {
            this.inclination = temp.inclination;

            this.ramp_angle_setting = temp.ramp_angle_setting;
            this.inclinationAndRampAngleSetting = true;
        }

        if (temp.resistanceLevel) {
            this.resistance_level = temp.resistance_level;
            this.resistanceLevel = true;
        }

        if (temp.insPower) {
            this.instantaneous_power = temp.instantaneous_power;
            this.insPower = true;
        }
        if (temp.avgPower) {
            this.average_power = temp.average_power;
            this.avgPower = true;
        }
        if (temp.energy) {
            this.total_energy = temp.total_energy;

            this.energy_per_hour = temp.energy_per_hour;

            this.energy_per_minute = temp.energy_per_minute;
            this.energy = true;
        }

        if (temp.heartRate) {
            this.heart_rate = temp.heart_rate;
            this.heartRate = true;
        }

        if (temp.metabolic) {
            this.metabolic_equivalent = temp.metabolic_equivalent;
            this.metabolic = true;
        }
        if (temp.elapsedTime) {
            this.elapsed_time = temp.elapsed_time;
            this.elapsedTime = true;
        }
        if (temp.remainingTime) {
            this.remaining_time = temp.remaining_time;
            this.remainingTime = true;
        }

        return null;
    }

    @Override
    public boolean hasMoreData() {
        return !InsSpeed;
    }

    @Override
    public byte[] getData() {
        return valueData;
    }

    @Override
    public boolean isInvalidData() {
        return isInvalidData;
    }

    @Override
    public void parseData(BluetoothGattCharacteristic characteristic, byte[] value) {
        init();
        value1 = value;
        valueData = value;
        parseData(value);
        for (int i = 0; i < value.length; i++) {
            Log.i(TAG, "CrossTrainerData: i = " + i + "; ox" + Integer.toHexString(ByteUtil.byte1ToInt(value[i])));
        }
    }

    private void init() {
        InsSpeed = false;
        avgSpeed = false;
        totalDistance = false;
        StepCountPresent = false;
        strideCount = false;
        elevationGain = false;
        inclinationAndRampAngleSetting = false;
        resistanceLevel = false;
        insPower = false;
        avgPower = false;
        energy = false;
        heartRate = false;
        metabolic = false;
        elapsedTime = false;
        remainingTime = false;
        movementDirection = false; //Forward or Backward

        expect_data_length = 0;

        isInvalidData = false;
    }
}
